# -*- coding: utf-8 -*-
'''
###########################################
#  Created on 2011-12-5
#  @author: cl.lam
#  Description:
###########################################
'''
import traceback
from hashlib import sha1
from datetime import datetime as dt, timedelta
from flask import session, g, render_template
from flask.helpers import url_for, flash
from werkzeug.utils import redirect
from flask.blueprints import Blueprint
from sqlalchemy.sql.expression import and_
from flask_wtf import Form, HiddenField, TextField, required, email, PasswordField, equal_to

from sys2do.util.decorator import templated
from sys2do.util.common import _g, _gld, upload, sendMail
from sys2do.model import db, User, Group, qry
from sys2do.constant import MESSAGE_ERROR, MSG_USER_NOT_EXIST, \
    MSG_WRONG_PASSWORD, MSG_RECORD_NOT_EXIST, MSG_NOT_ALL_PARAMS_OK, \
    LOG_TYPE_UPDATE, MSG_UPDATE_SUCC, MESSAGE_INFO, MSG_SERVER_ERROR, \
    MSG_EMAIL_NOT_EXIST, MSG_SEND_EMAIL_GET_PASSWORD, MSG_RESET_PASSWORD_LINK_EXPIRY, \
    MSG_RESET_PASSWORD_SUCCESS, MSG_WRONG_CAPTCHA
from flask.globals import request
from sqlalchemy.orm.exc import NoResultFound
from sys2do.model.master import ShopProfile, Shop
from sys2do.util.captcha import Captcha
from sys2do.util.logic_helper import getPermission, getCurrentUserID
from sys2do.model.system import SysLog


__all__ = ['bpAuth']

index_url = lambda : url_for( 'bpRoot.view', action = "index" )

bpAuth = Blueprint( 'bpAuth', __name__ )


@bpAuth.route( '/login', methods = ['GET', 'POST'] )
@templated( "login.html" )
def login():
    if session.get( 'login', None ): return redirect( index_url() )

    if request.method == "GET" : return {}

    msgs = []
    # 验证码输入是否正确
    captcha_input = _g( 'captcha' )
    captcha = Captcha( session = session )
    if not captcha_input or not captcha.validate( captcha_input ):
        msgs.append( MSG_WRONG_CAPTCHA )

    try:
        u = User.identify( _g( 'name' ) )
    except:
#         _error( traceback.print_exc() )
        flash( MSG_USER_NOT_EXIST, MESSAGE_ERROR )
    else:
        if not _g( 'password' ):
            msgs.append( MSG_WRONG_PASSWORD )

        if len( msgs ) > 0:
            flash( '<br/>'.join( msgs ), MESSAGE_ERROR )
            return redirect( url_for( 'bpAuth.login', next = _g( 'next' ) ) )
        elif not u.validatePassword( _g( 'password' ) ):
            flash( MSG_WRONG_PASSWORD, MESSAGE_ERROR )
            return redirect( url_for( 'bpAuth.login', next = _g( 'next' ) ) )
        else:
            # fill the info into the session
            loginUser( u )
            if _g( 'next' ) : return redirect( _g( 'next' ) )
            return redirect( index_url() )


@bpAuth.route( '/get_captcha' )
def get_captcha():
    captcha = Captcha( session = session, img_width = 120, img_height = 40 )
    return captcha.get_image()

@bpAuth.route( '/logout' )
def logout():
    logoutUser()
    return redirect( url_for( 'bpAuth.login' ) )

@bpAuth.route( '/forget_password', methods = ['GET', 'POST'] )
@templated( 'forget_password.html' )
def forget_password():
    '''
    找回密码流程：
        Step1:  .../auth/forget_password?t=input_email method=get 输入邮箱, 点击确认
        Step2:  .../auth/forget_password?t=input_email method=post 确认此邮箱的用户存在，
                通过sha1加密（验证用户身份）后和当前时间（验证链接是否失效）一起保存数据库，并发送邮件
        Step3:  .../auth/forget_password?t=reset_password&forget_pw_sha1=? method=get打开邮件，并点击链接，
                验证sha1和时间是否失效，通过则跳转到重置密码
        Step4:  .../auth/forget_password?t=reset_password method=post 重置密码，成功，重新登录系统
    '''
    t = _g( 't' )

    if t not in ['input_email', 'reset_password']:
        return redirect( url_for( 'bpAuth.login' ) )

    if t == 'input_email':    # 输入邮箱找回密码
        form = EmailForm()
        if request.method == 'POST' and form.validate_on_submit():
            email = form.email.data
            user = User.byEmail( email )
            if not user:
                flash( MSG_EMAIL_NOT_EXIST, MESSAGE_ERROR )
                return redirect( url_for( 'bpAuth.forget_password', t = t ) )
            user.forget_pw_time = dt.now()
            user.forget_pw_sha1 = sha1( email + str( user.forget_pw_time ) ).hexdigest()
            db.add( user )
            db.commit()
            reset_url = request.url_root[:-1] + url_for( 'bpAuth.forget_password', t = 'reset_password', forget_pw_sha1 = user.forget_pw_sha1 )
            subject = u'雅菡珠宝 - 找回密码'
            html = [
                u'亲爱的用户 %s 您好,' % user.name,
                u'<br/><div style="margin-left:24px;">请您点击下面的链接来找回密码： <a href="%s">%s</a>' % ( reset_url, reset_url ),
                u'<br/>为了保证您的帐号安全，该链接仅<font color="red">三天</font>内有效。',
                u'如果该链接已经失效，请重新找回密码。</div>'
            ]
            sendMail( [email], subject, None, '<br/>'.join( html ) )
            flash( MSG_SEND_EMAIL_GET_PASSWORD, MESSAGE_INFO )
        return {'form': form, 't': t}

    if t == 'reset_password':    # 重置密码
        if not _g( 'forget_pw_sha1' ): return redirect( url_for( 'bpAuth.login' ) )    # args:forget_pw_sha1 is required.
        form = ResetPasswordForm()
        form.forget_pw_sha1.data = _g( 'forget_pw_sha1' )
        if request.method == 'POST' and form.validate_on_submit():
            user = User.getBy( [User.forget_pw_sha1 == form.forget_pw_sha1.data] )
            if user and ( dt.now() - user.forget_pw_time ) < timedelta( days = 3 ) :    # 该用户存在，且链接有效期在3天之内
                user.password = form.password.data
                user.updateTime = dt.now()
                db.commit()
                flash( MSG_RESET_PASSWORD_SUCCESS, MESSAGE_INFO )
            else:
                flash( MSG_RESET_PASSWORD_LINK_EXPIRY, MESSAGE_ERROR )
                return redirect( url_for( 'bpAuth.forget_password', t = 'input_email' ) )
        return {'form': form, 't': t, 'forget_pw_sha1': _g( 'forget_pw_sha1' )}



def loginUser( u ):
    session['user_profile'] = u.populate()
    permissions = set()
    for g in u.groups:
        for p in g.permissions:
            permissions.add( p.name )
    session['user_profile']['groups'] = [g.name for g in u.groups]
    session['user_profile']['permissions'] = list( permissions )
    session['login'] = True
    # If set to True the session lives for permanent_session_lifetime seconds.
    # The default is 31 days. If set to False (which is the default) the session
    # will be deleted when the user closes the browser.
    session.permanent = False
    u.last_login = dt.now()

    gids = set( [unicode( g.id ) for g in u.groups] )
    shops = []
    if getPermission( 'SHOP_VIEW_ALL' ):
        shops = [( unicode( s.id ), unicode( s ) ) for s in qry( Shop ).filter( and_( Shop.active == 0 ) )]
    else:
        for sp in qry( ShopProfile ).filter( ShopProfile.active == 0 ):
            tmp = set( sp.groupIDs or [] )
            if len( gids.intersection( ( tmp ) ) ) > 0 : shops.append( ( unicode( sp.shopID ), unicode( sp.shop ) ) )
#     session['shops'] = shops
    if shops :
        session['currentShopID'] = shops[0][0]
    else:
        session['currentShopID'] = None
    db.commit()



def logoutUser():
    session.pop( 'login', None )
    session.pop( 'user_profile', None )
    session.pop( 'currentShopID', None )




@bpAuth.route( '/changePW', methods = ['GET', 'POST'] )
@templated( 'change_pw.html' )
def changePW():
    if request.method == 'GET':  return {}
    uid = getCurrentUserID()
    obj = User.get( uid )

    url = url_for( 'bpAuth.changePW' )
    if not obj :
        flash( MSG_RECORD_NOT_EXIST, MESSAGE_ERROR )
        return redirect( url )

    params = _gld( 'oldpassword', 'newpassword', 'repassword' )

    if not all( params.values() ):
        flash( MSG_NOT_ALL_PARAMS_OK, MESSAGE_ERROR )
    elif params['newpassword'] != params['repassword']:
        flash( MSG_NOT_ALL_PARAMS_OK, MESSAGE_ERROR )
    elif not obj.validatePassword( params['oldpassword'] ):
        flash( MSG_WRONG_PASSWORD, MESSAGE_ERROR )
    else:
        try:
            obj.password = params['newpassword']
            obj.updateTime = dt.now()
            db.add( SysLog( refClz = obj.__class__.__name__, type = LOG_TYPE_UPDATE, refID = obj.id, remark = '修改密码' ) )
            flash( MSG_UPDATE_SUCC, MESSAGE_INFO )
            db.commit()
        except:
            flash( MSG_SERVER_ERROR, MESSAGE_ERROR )
            db.rollback()

    return redirect( url )





@bpAuth.route( '/changePro', methods = ['GET', 'POST'] )
@templated( 'change_profile.html' )
def changePro():
    uid = getCurrentUserID()
    obj = User.get( uid )
    if request.method == 'GET':  return {'obj' : obj}

    params = _gld( 'email', 'phone', 'mobile' )
    try:
        obj.update( params )
        try:
            img = upload( 'imageURL' )
            obj.imageURL = img.url
        except:
            traceback.print_exc()
            pass

        db.add( SysLog( refClz = obj.__class__.__name__, type = LOG_TYPE_UPDATE, refID = obj.id, remark = '修改用户资料' ) )
        flash( MSG_UPDATE_SUCC, MESSAGE_INFO )
        db.commit()
    except:
        db.rollback()
        flash( MSG_SERVER_ERROR, MESSAGE_ERROR )

    return redirect( url_for( 'bpAuth.changePro' ) )



@bpAuth.route( '/viewLog', methods = ['GET', 'POST'] )
@templated( 'view_log.html' )
def viewLog():
    uid = getCurrentUserID()
    obj = User.get( uid )
    if not obj:
        flash( MSG_RECORD_NOT_EXIST, MESSAGE_ERROR )
        return redirect( url_for( 'bpRoot.view' ) )
    return {'obj' : obj}



class EmailForm( Form ):
    email = TextField( u'请您输入邮箱', validators = [required( message = u'必填' ), email( message = u'不是合法的邮箱地址' )] )


class ResetPasswordForm( Form ):
    forget_pw_sha1 = HiddenField( 'forget_pw_sha1', validators = [required( message = u'必填' )] )
    password = PasswordField( u'密码', validators = [required( message = u'必填' )] )
    password_confirm = PasswordField( u'重复密码', validators = [required( message = u'必填' ), equal_to( 'password', message = u'两次输入的密码不同' )] )
